#include <signal.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <pthread.h>
#include <fcntl.h>
#include "log.h"
#include "type.h"
#include "file.h"

U8 gDebug = 0;
S8 serviceIp[MAX_IP_NUM] = {0};

void hex_dump(const U8 *buf, U32 length)
{
    U32 i = 0;
    if (!gDebug) {
        return;
    }
    for (i = 0; i < length; i++) {
        printf("0x%02x ", (U8)buf[i]);
    }
    printf("\n");
}

S32 send_msg(const U8 *msg, U32 length, msg_channel_t *ch)
{
    S32 ret = 0;

    hex_dump(msg, length);

    ret = send(ch->fd, msg, length, 0);
    if (ret < 0) {
        ERROR("[%s:%u] send msg failed ret:%d", inet_ntoa(ch->cin.sin_addr), ntohs(ch->cin.sin_port), ret);
        return FAILURE;
    }

    return SUCCESS;
}

S32 check_msg_header(const U8 *msg, U32 length, msg_channel_t *ch)
{
    msg_header_t *pHeader = (msg_header_t *)msg;

    hex_dump(msg, length);

    DEBUG("[%s:%u] command type:0x%x response:%u",
        inet_ntoa(ch->cin.sin_addr), ntohs(ch->cin.sin_port), ntohs(pHeader->cmd_type), pHeader->response);

    if (ntohl(pHeader->head_flag) != 0xFFAA5500) {
        WARN("[%s:%u] msg illegal. header:0x%x", inet_ntoa(ch->cin.sin_addr), ntohs(ch->cin.sin_port), pHeader->head_flag);
        return FAILURE;
    }

    if (ntohs(pHeader->cmd_type) >= CMD_TYPE_ILLEGAL) {
        WARN("[%s:%u] illegal command type:0x%x", inet_ntoa(ch->cin.sin_addr), ntohs(ch->cin.sin_port), ntohs(pHeader->cmd_type));
        return FAILURE;
    }

    return SUCCESS;
}

S32 receive_msg(U8 *buf, msg_channel_t *ch)
{
    U32 requireLen = sizeof(msg_header_t);
    ssize_t buflen = 0;
    U32 offset = 0;
    msg_header_t *pHeader = NULL;

    while (1) {
        buflen = recv(ch->fd, &buf[offset], requireLen, 0);
        if(buflen < 0) {
            ERROR("recv message");
        } else if(buflen == 0) {
            INFO("[%s : %d] fd = %d APP exit", inet_ntoa(ch->cin.sin_addr), ntohs(ch->cin.sin_port), ch->fd);
            return FAILURE;
        }
        
        if (buflen < requireLen) {
            DEBUG("[%s : %d] buf size:%ld != requireLen:%d", inet_ntoa(ch->cin.sin_addr), ntohs(ch->cin.sin_port), buflen, requireLen);
            requireLen -= buflen;
            offset += buflen;
            continue;
        }
        
        pHeader = (msg_header_t *)buf;
        offset += buflen;
        
        DEBUG("[%s : %d] fd:%d msg length:%d",
            inet_ntoa(ch->cin.sin_addr), ntohs(ch->cin.sin_port), ch->fd, ntohs(pHeader->msg_length));
        
        if (offset != (sizeof(msg_header_t) + ntohs(pHeader->msg_length))) {
            DEBUG("offset:%d != sizeof(msg_header_t):%ld +  msg_length:%d", offset, sizeof(msg_header_t), ntohs(pHeader->msg_length));
            requireLen = ntohs(pHeader->msg_length);
            continue;
        }
        break;
    }
    return SUCCESS;
}

S32 process_login(U8 *msg, U32 length, msg_channel_t *ch)
{
    msg_header_t *pHeader = (msg_header_t *)msg;
    S8 userStr[MAX_USER_LENGTH] = {0};
    S8 passwdStr[MAX_PASSWD_LENGTH] = {0};
    S8 tmpStr[MAX_BUFFER_LENGTH] = {0};
    U32 offset = sizeof(msg_header_t);

    memcpy((void *)userStr, (void *)(msg + offset), MAX_USER_LENGTH);
    offset += MAX_USER_LENGTH;
    memcpy((void *)passwdStr, (void *)(msg + offset), MAX_PASSWD_LENGTH);
    offset += MAX_PASSWD_LENGTH;

    sprintf(tmpStr, "%s,%s", userStr, passwdStr);

    if (find_line_in_file(PASSWD_FILE, tmpStr) == FAILURE) {
        pHeader->response = FAILURE;
    } else {
        pHeader->response = 0;
    }
    pHeader->msg_length = 0;

    DEBUG("[%s:%u] user:%s passwd:%s %s\nlogin %s",
        inet_ntoa(ch->cin.sin_addr), ntohs(ch->cin.sin_port), userStr, passwdStr, tmpStr,
        (pHeader->response == 0)?"SUCCESS":"FAIL");

    send_msg(msg, sizeof(msg_header_t), ch);
    return SUCCESS;
}

S32 process_change_password(U8 *msg, U32 length, msg_channel_t *ch)
{
    msg_header_t *pHeader = (msg_header_t *)msg;
    S8 userStr[MAX_USER_LENGTH] = {0};
    S8 oldPasswdStr[MAX_PASSWD_LENGTH] = {0};
    S8 newPasswdStr[MAX_PASSWD_LENGTH] = {0};
    S8 tmpStr[MAX_BUFFER_LENGTH] = {0};
    U32 offset = sizeof(msg_header_t);
    S32 ret = 0;

    memcpy((void *)userStr, (void *)(msg + offset), MAX_USER_LENGTH);
    offset += MAX_USER_LENGTH;
    memcpy((void *)oldPasswdStr, (void *)(msg + offset), MAX_PASSWD_LENGTH);
    offset += MAX_USER_LENGTH;
    memcpy((void *)newPasswdStr, (void *)(msg + offset), MAX_PASSWD_LENGTH);

    sprintf(tmpStr, "%s,%s", userStr, oldPasswdStr);

    ret = find_line_in_file(PASSWD_FILE, tmpStr);
    if (ret == FAILURE) {
        pHeader->response = FAILURE;
    } else {
        memset(tmpStr, 0, MAX_BUFFER_LENGTH);
        sprintf(tmpStr, "%s,%s", userStr, newPasswdStr);
        ret = modify_line_in_file(PASSWD_FILE, ret, tmpStr);
        if (ret == FAILURE) {
            pHeader->response = FAILURE;
        } else {
            pHeader->response = 0;
        }
    }
    pHeader->msg_length = 0;

    DEBUG("[%s:%u] user:%s old passwd:%s new passwd:%s %s \nchange password %s",
        inet_ntoa(ch->cin.sin_addr), ntohs(ch->cin.sin_port), userStr, oldPasswdStr, newPasswdStr,
        tmpStr, (pHeader->response == 0)?"SUCCESS":"FAIL");

    send_msg(msg, sizeof(msg_header_t), ch);
    return SUCCESS;
}

S32 process_reset_password(U8 *msg, U32 length, msg_channel_t *ch)
{
    msg_header_t *pHeader = (msg_header_t *)msg;
    S8 userStr[MAX_USER_LENGTH] = {0};
    S8 emailStr[MAX_EMAIL_LENGTH] = {0};
    S8 tmpStr[MAX_BUFFER_LENGTH] = {0};
    U32 offset = sizeof(msg_header_t);
    S32 ret = 0;

    memcpy((void *)userStr, (void *)(msg + offset), MAX_USER_LENGTH);
    offset += MAX_USER_LENGTH;
    memcpy((void *)emailStr, (void *)(msg + offset), MAX_EMAIL_LENGTH);
    offset += MAX_EMAIL_LENGTH;

    sprintf(tmpStr, "%s,%s", emailStr, userStr);

    ret = find_line_in_file(PASSWD_FILE, tmpStr);
    if (ret == FAILURE) {
        pHeader->response = FAILURE;
    } else {
        memset(tmpStr, 0, MAX_BUFFER_LENGTH);
        sprintf(tmpStr, "%s,%s,%s", emailStr, userStr, userStr);
        ret = modify_line_in_file(PASSWD_FILE, ret, tmpStr);
        if (ret == FAILURE) {
            pHeader->response = FAILURE;
        } else {
            pHeader->response = 0;
        }
    }
    pHeader->msg_length = 0;

    DEBUG("[%s:%u] reset password. user:%s email:%s %s",
        inet_ntoa(ch->cin.sin_addr), ntohs(ch->cin.sin_port), userStr, emailStr,
        (pHeader->response == 0)?"SUCCESS":"FAIL");

    send_msg(msg, sizeof(msg_header_t), ch);
    return SUCCESS;
}

S32 process_get_all_monitor(U8 *msg, U32 length, msg_channel_t *ch)
{
    S8 tmpStr[MAX_BUFFER_LENGTH] = {0};
    U8 cmdAck[MAX_BUFFER_LENGTH] = {0};
    S8 monitorId[MONITOR_ID_NUM] = {0};
    S8 monitorNmae[MONITOR_NAME_NUM] = {0};
    S8 monitorNote[MONITOR_NOTE_NUM] = {0};
    U32 offset = sizeof(msg_header_t);
    msg_header_t *pHeader = (msg_header_t *)cmdAck;
    U32 totalLen = 0;
    U32 nameLen = 0;
    U32 noteLen = 0;
    U32 idLen = 0;

    memcpy((void *)cmdAck, (void *)msg, sizeof(msg_header_t));
    pHeader->msg_length = htons(MONITOR_ID_NUM + MONITOR_NAME_NUM + MONITOR_NOTE_NUM);

    FILE *fp = fopen(MONITOR_FILE, "r");
    if (fp == NULL) {
        WARN("Failed to open file %s", MONITOR_FILE);
        pHeader->response = FAILURE;
        send_msg(cmdAck, sizeof(msg_header_t), ch);
        return FAILURE;
    }

    while (fgets(tmpStr, MAX_BUFFER_LENGTH, fp) != NULL) {
        totalLen = strlen(tmpStr);
        S8 *str = strchr(tmpStr, ',');
        str++;
        S8 *noteStr = strchr(str, ',');
        noteStr++;
        noteLen = strlen(noteStr);
        memcpy(monitorNote, noteStr, noteLen);
        nameLen = strlen(str) - noteLen - 1;
        memcpy(monitorNmae, str, nameLen);
        idLen = totalLen - noteLen - nameLen - 2;
        memcpy(monitorId, tmpStr, idLen);

        memcpy((void *)(cmdAck + offset), (void *)monitorId, MONITOR_ID_NUM);
        offset += MONITOR_ID_NUM;
        memcpy((void *)(cmdAck + offset), (void *)monitorNmae, MONITOR_NAME_NUM);
        offset += MONITOR_NAME_NUM;
        memcpy((void *)(cmdAck + offset), (void *)monitorNote, MONITOR_NOTE_NUM);
        offset += MONITOR_NOTE_NUM;

        pHeader->response = 0;

        send_msg(cmdAck, offset, ch);
        DEBUG("[%s:%u] send monitor information %s %s %s",
            inet_ntoa(ch->cin.sin_addr), ntohs(ch->cin.sin_port), monitorId, monitorNmae, monitorNote);
        memset((void *)tmpStr, 0, MAX_BUFFER_LENGTH);
        offset = sizeof(msg_header_t);
    }
    fclose(fp);

    return SUCCESS;
}

S32 process_get_monitor_data(U8 *msg, U32 length, msg_channel_t *ch)
{
    S8 tmpStr[MAX_BUFFER_LENGTH] = {0};
    U8 cmdAck[MAX_BUFFER_LENGTH] = {0};
    S8 monitorId[MONITOR_ID_NUM] = {0};
    S8 monitorDataFile[MAX_BUFFER_LENGTH] = {0};
    U32 offset = sizeof(msg_header_t);
    msg_header_t *pHeader = (msg_header_t *)cmdAck;
    S32 dataSize = 0;
    U32 year = 0, month = 0, day = 0;
    U32 temperature = 0, temperature_h = 0, temperature_l = 0;
    U32 humidity = 0, humidity_h = 0, humidity_l = 0;
    U32 co2 = 0, co2_h = 0, co2_l = 0;
    U32 luminance = 0, luminance_h = 0, luminance_l = 0;
    U32 soilHumidity = 0, soilHumidity_h = 0, soilHumidity_l = 0;
    U16 value = 0;

    memcpy((void *)monitorId, (void *)(msg + offset), MONITOR_ID_NUM);

    sprintf(monitorDataFile, "%s.txt", monitorId);
    FILE *fp = fopen(monitorDataFile, "r");
    if (fp == NULL) {
        WARN("Failed to open file %s", monitorDataFile);
        pHeader->response = FAILURE;
        send_msg(cmdAck, sizeof(msg_header_t), ch);
        return FAILURE;
    }

    /*calculate the data size of one line*/
    while (fgets(tmpStr, MAX_BUFFER_LENGTH, fp) != NULL) {
        dataSize = strlen(tmpStr);
        break;
    }
    /*get the latest data*/
    if (fseek(fp, -1*(long)dataSize, SEEK_END) == 0) {
        if (fgets(tmpStr, MAX_BUFFER_LENGTH, fp) != NULL) {
            DEBUG("%s", tmpStr);
            sscanf(tmpStr, "%04d-%02d-%02d temperature:%02d.%02d humidity:%02d.%02d CO2:%02d.%02d luminance:%02d.%02d soil humidity:%02d.%02d",
                &year, &month, &day,
                &temperature_h, &temperature_l,
                &humidity_h, &humidity_l,
                &co2_h, &co2_l,
                &luminance_h, &luminance_l,
                &soilHumidity_h, &soilHumidity_l);

                temperature = temperature_h*100 + temperature_l;
                humidity = humidity_h * 100 + humidity_l;
                co2 = co2_h * 100 + co2_l;
                luminance = luminance_h * 100 + luminance_l;
                soilHumidity = soilHumidity_h * 100 + soilHumidity_l;

                memcpy((void *)cmdAck, (void *)msg, sizeof(msg_header_t));
                memcpy((void *)(cmdAck + offset), (void *)monitorId, MONITOR_ID_NUM);
                offset += MONITOR_ID_NUM;
                value = htons((U16)temperature);
                memcpy((void *)(cmdAck + offset), (void *)&value, 2);
                offset += 2;
                value = htons((U16)humidity);
                memcpy((void *)(cmdAck + offset), (void *)&value, 2);
                offset += 2;
                value = htons((U16)co2);
                memcpy((void *)(cmdAck + offset), (void *)&value, 2);
                offset += 2;
                value = htons((U16)luminance);
                memcpy((void *)(cmdAck + offset), (void *)&value, 2);
                offset += 2;
                value = htons((U16)soilHumidity);
                memcpy((void *)(cmdAck + offset), (void *)&value, 2);
                offset += 2;

                pHeader->response = 0;
                pHeader->msg_length = htons(offset - sizeof(msg_header_t));

                send_msg(cmdAck, offset, ch);
                DEBUG("[%s:%u] send monitor data temperature:%d humidity:%d CO2:%d luminance:%d soil Humidity:%d",
                    inet_ntoa(ch->cin.sin_addr), ntohs(ch->cin.sin_port),
                    temperature, humidity, co2, luminance, soilHumidity);
        }
    }
    fclose(fp);

    return SUCCESS;
}

S32 process_get_history_data(U8 *msg, U32 length, msg_channel_t *ch)
{
    S8 tmpStr[MAX_BUFFER_LENGTH] = {0};
    U8 cmdAck[MAX_BUFFER_LENGTH] = {0};
    S8 monitorId[MONITOR_ID_NUM] = {0};
    S8 monitorDataFile[MAX_BUFFER_LENGTH] = {0};
    U32 offset = sizeof(msg_header_t);
    msg_header_t *pHeader = (msg_header_t *)cmdAck;
    S32 dataSize = 0;
    U32 year = 0, month = 0, day = 0;
    U32 temperature = 0, temperature_h = 0, temperature_l = 0;
    U32 humidity = 0, humidity_h = 0, humidity_l = 0;
    U32 co2 = 0, co2_h = 0, co2_l = 0;
    U32 luminance = 0, luminance_h = 0, luminance_l = 0;
    U32 soilHumidity = 0, soilHumidity_h = 0, soilHumidity_l = 0;
    U32 dataType = 0;
    U32 dataTimeType = 0;
    U16 value = 0;
    S64 dataOffset = 0;

    memcpy((void *)monitorId, (void *)(msg + offset), MONITOR_ID_NUM);
    offset += MONITOR_ID_NUM;
    dataType = *(msg + offset);
    offset++;
    dataTimeType = *(msg + offset);
    offset++;

    memcpy((void *)cmdAck, (void *)msg, offset);

    sprintf(monitorDataFile, "%s.txt", monitorId);
    FILE *fp = fopen(monitorDataFile, "r");
    if (fp == NULL) {
        WARN("Failed to open file %s", monitorDataFile);
        pHeader->response = FAILURE;
        send_msg(cmdAck, sizeof(msg_header_t), ch);
        fclose(fp);
        return FAILURE;
    }

    /*calculate the data size of one line*/
    while (fgets(tmpStr, MAX_BUFFER_LENGTH, fp) != NULL) {
        dataSize = strlen(tmpStr);
        break;
    }

    DEBUG("data type:%d data time type:%d", dataType, dataTimeType);
    if (dataTimeType == 1) {
        dataOffset = -7*(long)dataSize;
    } else if (dataTimeType == 2) {
        dataOffset = -30*(long)dataSize;
    } else if (dataTimeType == 3) {
        dataOffset = -365*(long)dataSize;
    } else {
        WARN("illegal data time type:%d", dataTimeType);
        pHeader->response = FAILURE;
        send_msg(cmdAck, sizeof(msg_header_t), ch);
        return FAILURE;
    }

    /*get the latest data*/
    if (fseek(fp, dataOffset, SEEK_END) == 0) {
        while (fgets(tmpStr, MAX_BUFFER_LENGTH, fp) != NULL) {
            DEBUG("%s", tmpStr);
            sscanf(tmpStr, "%04d-%02d-%02d temperature:%02d.%02d humidity:%02d.%02d CO2:%02d.%02d luminance:%02d.%02d soil humidity:%02d.%02d",
                &year, &month, &day,
                &temperature_h, &temperature_l,
                &humidity_h, &humidity_l,
                &co2_h, &co2_l,
                &luminance_h, &luminance_l,
                &soilHumidity_h, &soilHumidity_l);

                temperature = temperature_h*100 + temperature_l;
                humidity = humidity_h * 100 + humidity_l;
                co2 = co2_h * 100 + co2_l;
                luminance = luminance_h * 100 + luminance_l;
                soilHumidity = soilHumidity_h * 100 + soilHumidity_l;

                if (dataType == 1) {
                    value = htons((U16)temperature);
                } else if (dataType == 2) {
                    value = htons((U16)humidity);
                } else if (dataType == 3) {
                    value = htons((U16)co2);
                } else if (dataType == 4) {
                    value = htons((U16)luminance);
                } else if (dataType == 5) {
                    value = htons((U16)soilHumidity);
                }
                memcpy((void *)(cmdAck + offset), (void *)&value, 2);
                offset += 2;
                DEBUG("[%s:%u] send monitor history data temperature:%d humidity:%d CO2:%d luminance:%d soil Humidity:%d",
                    inet_ntoa(ch->cin.sin_addr), ntohs(ch->cin.sin_port),
                    temperature, humidity, co2, luminance, soilHumidity);
        }
    } else {
        WARN("get history data failed");
        pHeader->response = FAILURE;
        send_msg(cmdAck, sizeof(msg_header_t), ch);
        fclose(fp);
        return FAILURE;
    }

    pHeader->response = 0;
    pHeader->msg_length = htons(offset - sizeof(msg_header_t));
    
    send_msg(cmdAck, offset, ch);

    fclose(fp);

    return SUCCESS;
}

S32 check_monitor_id(S8 *id)
{
    S8 tmpStr[MAX_BUFFER_LENGTH] = {0};
    S8 monitorId[MONITOR_ID_NUM] = {0};
    S8 monitorNmae[MONITOR_NAME_NUM] = {0};
    S8 monitorNote[MONITOR_NOTE_NUM] = {0};
    U32 totalLen = 0;
    U32 nameLen = 0;
    U32 noteLen = 0;
    U32 idLen = 0;
    S32 ret = FAILURE;

    FILE *fp = fopen(MONITOR_FILE, "r");
    if (fp == NULL) {
        WARN("Failed to open file %s", MONITOR_FILE);
        return FAILURE;
    }

    while (fgets(tmpStr, MAX_BUFFER_LENGTH, fp) != NULL) {
        totalLen = strlen(tmpStr);
        S8 *str = strchr(tmpStr, ',');
        str++;
        S8 *noteStr = strchr(str, ',');
        noteStr++;
        noteLen = strlen(noteStr);
        memcpy(monitorNote, noteStr, noteLen);
        nameLen = strlen(str) - noteLen - 1;
        memcpy(monitorNmae, str, nameLen);
        idLen = totalLen - noteLen - nameLen - 2;
        memcpy(monitorId, tmpStr, idLen);

        if (!strcmp(monitorId, id)) {
            ret = SUCCESS;
            break;
        }
        memset((void *)tmpStr, 0, MAX_BUFFER_LENGTH);
    }
    fclose(fp);

    return ret;
}

S32 process_set_water_valve(U8 *msg, U32 length, msg_channel_t *ch)
{
    S8 id[MONITOR_ID_NUM] = {0};
    U32 offset = sizeof(msg_header_t);
    msg_header_t *pHeader = (msg_header_t *)msg;

    memcpy((void *)id, (void *)(msg + offset), MONITOR_ID_NUM);

    if (check_monitor_id(id) == FAILURE) {
        pHeader->response = FAILURE;
    } else {
        pHeader->response = 0;
    }
    send_msg(msg, offset, ch);
    DEBUG("[%s:%u] send set water valve ack", inet_ntoa(ch->cin.sin_addr), ntohs(ch->cin.sin_port));

    return SUCCESS;
}

S32 process_get_plan(U8 *msg, U32 length, msg_channel_t *ch)
{
    U8 cmdAck[MAX_BUFFER_LENGTH] = {0};
    S8 tmpStr[MAX_BUFFER_LENGTH] = {0};
    S8 id[MONITOR_ID_NUM] = {0};
    U32 offset = sizeof(msg_header_t);
    msg_header_t *pHeader = (msg_header_t *)cmdAck;
    plan_t plan = {0};

    memcpy((void *)id, (void *)(msg + offset), MONITOR_ID_NUM);
    memcpy((void *)cmdAck, (void *)msg, sizeof(msg_header_t));
    memcpy((void *)(cmdAck + offset), (void *)(msg + offset), MONITOR_ID_NUM);
    offset += MONITOR_ID_NUM;

    if (check_monitor_id(id) == FAILURE) {
        pHeader->response = FAILURE;
        send_msg(cmdAck, offset, ch);
        return FAILURE;
    }

    DEBUG("[%s:%u] get plan type:%u",
        inet_ntoa(ch->cin.sin_addr), ntohs(ch->cin.sin_port), plan.type);

    sprintf(tmpStr, "%s_plan.txt", id);
    FILE *fp = fopen(tmpStr, "r");
    if (fp == NULL) {
        WARN("Failed to open file %s", tmpStr);
        pHeader->response = FAILURE;
        send_msg(cmdAck, sizeof(msg_header_t), ch);
        return FAILURE;
    }
    if (fread((void *)&plan, 1, sizeof(plan_t), fp) != sizeof(plan_t)) {
        WARN("Failed to read file %s", tmpStr);
        pHeader->response = FAILURE;
        send_msg(cmdAck, sizeof(msg_header_t), ch);
        return FAILURE;
    }

    if (plan.type == 0x1) {
        /*手动灌溉*/
        cmdAck[offset] = 0x1;
        offset++;

        pHeader->msg_length = htons(offset);
    } else if (plan.type == 0x2) {
        /*定时灌溉*/
        cmdAck[offset] = 0x2;
        offset++;
        cmdAck[offset] = plan.timePlan.water_valve_control;
        offset++;
        cmdAck[offset] = plan.timePlan.time_type;
        offset++;
        memcpy((void *)(cmdAck + offset), (void *)plan.timePlan.time_start, TIME_NUM);
        offset += TIME_NUM;
        memcpy((void *)(cmdAck + offset), (void *)plan.timePlan.time_hold, TIME_NUM);
        offset += TIME_NUM;

        pHeader->msg_length = htons(offset);
    } else if (plan.type == 0x3) {
        /*阈值灌溉*/
        cmdAck[offset] = 0x3;
        offset++;
        cmdAck[offset] = plan.thresholdPlan.water_valve_control;
        offset++;
        memcpy((void *)(cmdAck + offset), (void *)plan.thresholdPlan.time_hold, TIME_NUM);
        offset += TIME_NUM;
        cmdAck[offset] = plan.thresholdPlan.temperature_vaild;
        offset++;
        cmdAck[offset] = plan.thresholdPlan.temperature_relation;
        offset++;
        memcpy((void *)(cmdAck + offset), (void *)&plan.thresholdPlan.temperature_threshold, 2);
        offset += 2;
        cmdAck[offset] = plan.thresholdPlan.humidity_vaild;
        offset++;
        cmdAck[offset] = plan.thresholdPlan.humidity_relation;
        offset++;
        memcpy((void *)(cmdAck + offset), (void *)&plan.thresholdPlan.humidity_threshold, 2);
        offset += 2;
        cmdAck[offset] = plan.thresholdPlan.co2_vaild;
        offset++;
        cmdAck[offset] = plan.thresholdPlan.co2_relation;
        offset++;
        memcpy((void *)(cmdAck + offset), (void *)&plan.thresholdPlan.co2_threshold, 2);
        offset += 2;
        cmdAck[offset] = plan.thresholdPlan.luminance_vaild;
        offset++;
        cmdAck[offset] = plan.thresholdPlan.luminance_relation;
        offset++;
        memcpy((void *)(cmdAck + offset), (void *)&plan.thresholdPlan.luminance_threshold, 2);
        offset += 2;
        cmdAck[offset] = plan.thresholdPlan.soil_humidity_vaild;
        offset++;
        cmdAck[offset] = plan.thresholdPlan.soil_humidity_relation;
        offset++;
        memcpy((void *)(cmdAck + offset), (void *)&plan.thresholdPlan.soil_humidity_threshold, 2);
        offset += 2;

        pHeader->msg_length = htons(offset);
    }

    send_msg(cmdAck, offset, ch);

    return SUCCESS;
}

S32 process_set_plan(U8 *msg, U32 length, msg_channel_t *ch)
{
    S8 tmpStr[MAX_BUFFER_LENGTH] = {0};
    S8 id[MONITOR_ID_NUM] = {0};
    U32 offset = sizeof(msg_header_t);
    msg_header_t *pHeader = (msg_header_t *)msg;
    U8 type = 0;
    plan_t plan = {0};

    memcpy((void *)id, (void *)(msg + offset), MONITOR_ID_NUM);
    offset += MONITOR_ID_NUM;
    if (check_monitor_id(id) == FAILURE) {
        pHeader->response = FAILURE;
        send_msg(msg, offset, ch);
        return FAILURE;
    }

    type = *(msg + offset);
    offset++;

    DEBUG("[%s:%u] set plan type:%u",
        inet_ntoa(ch->cin.sin_addr), ntohs(ch->cin.sin_port), type);

    if (type == 0x1) {
        /*手动灌溉*/
        plan.type = 0x1;
    } else if (type == 0x2) {
        /*定时灌溉*/
        plan.type = 0x2;

        plan.timePlan.water_valve_control = *(msg + offset);
        offset++;
        plan.timePlan.time_type = *(msg + offset);
        offset++;
        memcpy((void *)plan.timePlan.time_start, (void *)(msg + offset), TIME_NUM);
        offset += TIME_NUM;
        memcpy((void *)plan.timePlan.time_hold, (void *)(msg + offset), TIME_NUM);
        offset += TIME_NUM;
    } else if (type == 0x3) {
        /*阈值灌溉*/
        plan.type = 0x3;

        plan.thresholdPlan.water_valve_control = *(msg + offset);
        offset++;
        memcpy((void *)plan.thresholdPlan.time_hold, (void *)(msg + offset), TIME_NUM);
        offset += TIME_NUM;
        plan.thresholdPlan.temperature_vaild = *(msg + offset);
        offset++;
        plan.thresholdPlan.temperature_relation = *(msg + offset);
        offset++;
        plan.thresholdPlan.temperature_threshold = ntohs(*(U16 *)(msg + offset));
        offset += 2;

        plan.thresholdPlan.humidity_vaild = *(msg + offset);
        offset++;
        plan.thresholdPlan.humidity_relation = *(msg + offset);
        offset++;
        plan.thresholdPlan.humidity_threshold = ntohs(*(U16 *)(msg + offset));
        offset += 2;

        plan.thresholdPlan.co2_vaild = *(msg + offset);
        offset++;
        plan.thresholdPlan.co2_relation = *(msg + offset);
        offset++;
        plan.thresholdPlan.co2_threshold = ntohs(*(U16 *)(msg + offset));
        offset += 2;

        plan.thresholdPlan.luminance_vaild = *(msg + offset);
        offset++;
        plan.thresholdPlan.luminance_relation = *(msg + offset);
        offset++;
        plan.thresholdPlan.luminance_threshold = ntohs(*(U16 *)(msg + offset));
        offset += 2;

        plan.thresholdPlan.soil_humidity_vaild = *(msg + offset);
        offset++;
        plan.thresholdPlan.soil_humidity_relation = *(msg + offset);
        offset++;
        plan.thresholdPlan.soil_humidity_threshold = ntohs(*(U16 *)(msg + offset));
        offset += 2;
    }

    sprintf(tmpStr, "%s_plan.txt", id);
    FILE *fp = fopen(tmpStr, "w");
    if (fp == NULL) {
        WARN("Failed to open file %s", tmpStr);
        pHeader->response = FAILURE;
        send_msg(msg, sizeof(msg_header_t), ch);
        return FAILURE;
    }
    if (fwrite((void *)&plan, 1, sizeof(plan_t), fp) != sizeof(plan_t)) {
        WARN("Failed to write file %s", tmpStr);
        pHeader->response = FAILURE;
        send_msg(msg, sizeof(msg_header_t), ch);
        return SUCCESS;
    }
    pHeader->response = 0;
    send_msg(msg, sizeof(msg_header_t), ch);

    return SUCCESS;
}

S32 app_msg_handler(U8 *msg, U32 length, msg_channel_t *ch)
{
    msg_header_t *pHeader = (msg_header_t *)msg;

    if (check_msg_header(msg, length, ch) != SUCCESS) {
        return FAILURE;
    }

    switch (ntohs(pHeader->cmd_type)) {
        case CMD_TYPE_LOGIN: {
            process_login(msg, length, ch);
            break;
        }
        case CMD_TYPE_CHANGE_PASSWORD: {
            process_change_password(msg, length, ch);
            break;
        }
        case CMD_TYPE_RESET_PASSWORD: {
            process_reset_password(msg, length, ch);
            break;
        }
        case CMD_TYPE_GET_ALL_MONITOR: {
            process_get_all_monitor(msg, length, ch);
            break;
        }
        case CMD_TYPE_GET_MONITOR_DATA: {
            process_get_monitor_data(msg, length, ch);
            break;
        }
        case CMD_TYPE_GET_HISTORY_DATA: {
            process_get_history_data(msg, length, ch);
            break;
        }
        case CMD_TYPE_SET_WATER_VALVE: {
            process_set_water_valve(msg, length, ch);
            break;
        }
        case CMD_TYPE_GET_PLAN: {
            process_get_plan(msg, length, ch);
            break;
        }
        case CMD_TYPE_SET_PLAN: {
            process_set_plan(msg, length, ch);
            break;
        }
        default: {
            WARN("[%s:%u] unknown command type:0x%x", inet_ntoa(ch->cin.sin_addr), ntohs(ch->cin.sin_port), ntohs(pHeader->cmd_type));
            break;
        }
    }
    return SUCCESS;
}

void *app_msg_recv_thread(void *arg)
{
    pthread_detach(pthread_self());
    S32 fd = ((msg_channel_t *)arg)->fd;
    U8 buf[MAX_BUFFER_LENGTH] = {0};
    msg_header_t *pHeader = (msg_header_t *)buf;
    bzero(buf, sizeof(buf));

    while(1) {
        //循环接收
        if (receive_msg(buf, (msg_channel_t *)arg) != SUCCESS) {
            break;
        }

        app_msg_handler(buf, sizeof(msg_header_t) + ntohs(pHeader->msg_length), (msg_channel_t *)arg);
        bzero(buf, sizeof(buf));
    }

    close(fd);
    pthread_exit(NULL);
}

S32 tcp_service_init(const S8 *ip, U32 port)
{
    S32 reuse = 1;
    S32 serviceFd = socket(AF_INET, SOCK_STREAM, 0);
    if(serviceFd < 0) {
        ERROR("socket");
        return FAILURE;
    }

    //允许端口快速重用
    if(setsockopt(serviceFd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) {
        ERROR("setsockopt");
        return FAILURE;
    }

    //绑定客户端的地址信息结构体（非必须绑定)
    //填充要连接的服务器的地址信息结构体
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(port);
    sin.sin_addr.s_addr = inet_addr(ip);

    //将地址信息结构体绑定到套接字上
    bind(serviceFd,(struct sockaddr *)&sin,sizeof(sin));
    if(bind < 0) {
        ERROR("bind");
        return FAILURE;
    }
    INFO("bind %s:%d success", ip, port);

    //将套接字设置为被动监听状态，让内核去监听是否有客户端链接
    if(listen(serviceFd,10) < 0) {
        ERROR("listen");
        return FAILURE;
    }
    INFO("start listen port %d", port);

    return serviceFd;
}

void *apps_thread(void *arg)
{
    pthread_t tid;
    msg_channel_t msg;
    S32 fd = 0;
    struct sockaddr_in cin;
    socklen_t addrlen = sizeof(cin);
    S32 serviceFd = tcp_service_init(serviceIp, APPS_PORT);
    if (serviceFd == FAILURE) {
        return (void *)FAILURE;
    }

    while(1) {
        fd = accept(serviceFd, (struct sockaddr*)&cin, &addrlen);
        if(fd < 0) {
            ERROR("fd");
            return (void *)FAILURE;
        }
    
        msg.fd = fd;
        msg.cin = cin;
        if(pthread_create(&tid, NULL, app_msg_recv_thread, (void *)&msg) != 0) {
            ERROR("pthread_create app_msg_recv_thread");
            return (void *)FAILURE;
        }
    }
    
    close(serviceFd);
    pthread_exit(NULL);
}

S32 main(S32 argc, char *argv[])
{
    pthread_t tid;
    S32 opt, args;
    S8 optString[MAX_BUFFER_LENGTH] = "gi:";

    ENTER();

    if (argc < 2) {
        ERROR("please use -i ip");
        return FAILURE;
    }
    while ((opt = getopt(argc, argv, &optString[0])) != -1) {
        switch (opt) {
            case 'g':
                gDebug = 1;
                args++;
                break;
            case 'i':
                memset((void *)serviceIp, 0, MAX_IP_NUM);
                memcpy((void *)serviceIp, optarg, strlen(optarg));
                break;
            default:
                break;
        }
    }

    if(pthread_create(&tid, NULL, apps_thread, NULL) != 0) {
        ERROR("create devices thread fail");
        return FAILURE;
    }

    while(1) {};

    EXIT();

    return SUCCESS;
}
